﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RuleOfReady.Core.Utility
{
    /// <summary>
    /// A Random number generator that's seeded randomly.
    /// </summary>
    public class ThreadSafeRandom
    {

        #region Private Fields

        /// <summary>
        /// Source of seeds
        /// </summary>
        private static readonly Random globalRandom = new Random();

        /// <summary>
        /// A field for storing the internal (local) random number generator
        /// </summary>
        [ThreadStatic]
        private static Random localRandom;

        #endregion

        #region Constructor

        /// <summary>
        /// Create a new pseudo-random number generator that is seeded randomly
        /// </summary>
        public ThreadSafeRandom()
        {
            if (ThreadSafeRandom.localRandom == null)
            {
                int seed;
                lock (ThreadSafeRandom.globalRandom)
                {
                    seed = ThreadSafeRandom.globalRandom.Next();
                }
                localRandom = new Random(seed);
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Returns a nonnegative number
        /// </summary>
        /// <returns>A 32-bit signed integer greater than or equal to zero and less than int.MaxValue.</returns>
        public int Next()
        {
            return ThreadSafeRandom.localRandom.Next();
        }

        /// <summary>
        /// Returns a nonnegative random number less than the specified maximum.
        /// </summary>
        /// <param name="maxValue">
        /// The exclusive upper bound of the random number to be generated. 
        /// maxValue must be greater than or equal to zero. 
        /// </param>
        /// <returns>
        /// A 32-bit signed integer greater than or equal to zero, and less than maxValue; 
        /// that is, the range of return values ordinarily includes zero but not maxValue. 
        /// However, if maxValue equals zero, maxValue is returned.
        /// </returns>
        public int Next(int maxValue)
        {
            return ThreadSafeRandom.localRandom.Next(maxValue);
        }

        /// <summary>
        /// Returns a random number within a specified range.
        /// </summary>
        /// <param name="minValue">The inclusive lower bound of the random number returned. </param>
        /// <param name="maxValue">
        /// The exclusive upper bound of the random number returned. 
        /// maxValue must be greater than or equal to minValue.
        /// </param>
        /// <returns>
        ///  32-bit signed integer greater than or equal to minValue and less than maxValue; 
        ///  that is, the range of return values includes minValue but not maxValue. 
        ///  If minValue equals maxValue, minValue is returned.
        ///  </returns>
        public int Next(int minValue, int maxValue)
        {
            return ThreadSafeRandom.localRandom.Next(minValue, maxValue);
        }

        /// <summary>
        /// Fills the elements of a specified array of bytes with random numbers.
        /// </summary>
        /// <param name="buffer">An array of bytes to contain random numbers. </param>
        public void NextBytes(byte[] buffer)
        {
            ThreadSafeRandom.localRandom.NextBytes(buffer);
        }

        /// <summary>
        /// Returns a random number between 0.0 and 1.0.
        /// </summary>
        /// <returns>
        /// A double-precision floating point number greater than or equal to 0.0, 
        /// and less than 1.0.
        /// </returns>
        public double NextDouble()
        {
            return ThreadSafeRandom.localRandom.NextDouble();
        }

        #endregion
    }
}
